﻿#include "PTWindowImpl.h"
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <xcb/xcb_keysyms.h>
#include <X11/keysym.h>

void PTWindowImpl::HandleGet(uint32_t *pChanged, PTHandleDisplay *pHandleDisplay, PTHandleWindow *pHandleWindow)
{
	::PTSpinLockEnterSleep(&m_HandleSpinLock);//写者较读者优先
	if (((*pChanged) = m_bHandleChanged) != 0U)
	{
		m_bHandleChanged = 0U;
		(*pHandleDisplay) = m_HandleDisplay;
		(*pHandleWindow) = m_HandleWindow;
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

void PTWindowImpl::SizeGet(uint32_t *pChanged, uint32_t *pWidth, uint32_t *pHeight, uint32_t *pLayer)
{
	::PTSpinLockEnterSleep(&m_SizeSpinLock);//写者较读者优先
	if (((*pChanged) = m_bSizeChanged) != 0U)
	{
		m_bSizeChanged = 0U;
		(*pWidth) = m_Width;
		(*pHeight) = m_Height;
		(*pLayer) = 1U;
	}
	::PTSpinLockLeave(&m_SizeSpinLock);
}

void PTWindowImpl::HandleSet(xcb_connection_t *HandleDisplay, xcb_window_t HandleWindow)
{
	::PTSpinLockEnterBusy(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleDisplay != HandleDisplay || m_HandleWindow != HandleWindow)
	{
		m_bHandleChanged = 1U;
		m_HandleDisplay = HandleDisplay;
		m_HandleWindow = HandleWindow;
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

void PTWindowImpl::SizeSet(uint32_t width, uint32_t Height)
{
	::PTSpinLockEnterBusy(&m_SizeSpinLock);//写者较读者优先
	if (m_Width != width || m_Height != Height)
	{
		m_bSizeChanged = 1U;
		m_Width = width;
		m_Height = Height;
	}
	::PTSpinLockLeave(&m_SizeSpinLock);
}

void PTWindowImpl::Construct()
{
	//Handle
	::PTSpinLockCreate(&m_HandleSpinLock);
	m_bHandleChanged = 0U;
	m_HandleDisplay = NULL;
	m_HandleWindow = ~0U;
	//Size
	::PTSpinLockCreate(&m_SizeSpinLock);
	m_bSizeChanged = 0U;
	m_Width = 0U;
	m_Height = 0U;
}


void PTWindowImpl::Destruct()
{
	::PTSpinLockDestory(&m_HandleSpinLock);
	::PTSpinLockDestory(&m_SizeSpinLock);
}

void PTWindowImpl::HandleSetAndDestory()
{
	//assert(m_HandleWindow == HandleWindow);
	::PTSpinLockEnterBusy(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleWindow != ~0U)
	{
		m_bHandleChanged = 1U;
		m_HandleWindow = ~0U;
		//BOOL wbResult = ::DestroyWindow(HandleWindow); //DestroyWindow不允许其它线程调用
		//assert(wbResult != 0);
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

void PTWindowImpl::HandleGetAndDestory()
{
	::PTSpinLockEnterSleep(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleWindow != ~0U)
	{
		//BOOL wbResult = ::PostMessageW(m_HandleWindow, (WM_APP + 13U), 0, 0); //DestroyWindow不允许其它线程调用
		//assert(wbResult != 0);
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

int main(int argc, char *argv[])
{
	xcb_connection_t *hDisplay;
	int iScreenPreferred;
	{
		char *DisplayName = NULL;
		for (int i = 1; i < argc; ++i)
		{
			DisplayName = ::strstr(argv[i], "-PTDisplay");
			if (DisplayName != NULL)
			{
				break;
			}
		}
		if (DisplayName != NULL)
		{
			assert((*(DisplayName + 10)) == '=');
			DisplayName += 11;
		}
		hDisplay = ::xcb_connect(DisplayName, &iScreenPreferred);
	}
	assert(::xcb_connection_has_error(hDisplay) == 0);

	xcb_window_t hWnd;
	{
		bool bEditor = false;
		for (int i = 1; i < argc; ++i)
		{
			if (::strstr(argv[i], "-PTEditor") != NULL)
			{
				bEditor = true;
				break;
			}
		}
		if (bEditor)
		{
			//编辑器模式
			//编辑器进程和运行时进程分离
			//Visual Studio的3D场景(.fbx)编辑器
			//"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.exe"
			//"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\Microsoft\VsGraphics\vsgraphics.exe"
			char *EditorWindowName = NULL;
			for (int i = 1; i < argc; ++i)
			{
				EditorWindowName = ::strstr(argv[i], "-PTWindow");
				if (EditorWindowName != NULL)
				{
					break;
				}
			}
			if (EditorWindowName == NULL)
			{
				hWnd = ~0U;
			}
			else
			{
				assert((*(EditorWindowName + 9)) == '=');
				EditorWindowName += 10;

				xcb_window_t hParent;
				hParent = static_cast<xcb_window_t>(::atoi(EditorWindowName));

				xcb_get_geometry_reply_t *pWG;
				xcb_get_geometry_cookie_t cookieWG;
				cookieWG = ::xcb_get_geometry_unchecked(hDisplay, hParent);
				//WG-Alloc
				pWG = ::xcb_get_geometry_reply(hDisplay, cookieWG, NULL);
				if (pWG == NULL)
				{
					hWnd = ~0U;
				}
				else
				{
					//CreateWindowExW
					xcb_window_t XID = ::xcb_generate_id(hDisplay);
					uint32_t valuemaskCW = XCB_CW_EVENT_MASK;
					uint32_t valuelistCW[1] = { XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE };
					xcb_void_cookie_t cookieCW = ::xcb_create_window_checked(hDisplay, XCB_COPY_FROM_PARENT, XID, hParent, 0, 0, pWG->width, pWG->height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, valuemaskCW, valuelistCW);
					xcb_generic_error_t *pErrorCW = ::xcb_request_check(hDisplay, cookieCW);//隐式xcb_flush
					if (pErrorCW != NULL)
					{
						hWnd = ~0U;
					}
					else
					{
						//ShowWindow
						xcb_void_cookie_t cookieMW = ::xcb_map_window_checked(hDisplay, XID);
						xcb_generic_error_t *pErrorMW = ::xcb_request_check(hDisplay, cookieMW);//隐式xcb_flush
						if (pErrorMW != NULL)
						{
							hWnd = ~0U;
						}
						else
						{
							hWnd = XID;
						}
					}
				}
				//WG-Free
				::free(pWG);
			}
		}
		else
		{
			//独立模式
			const xcb_setup_t *pDisplaySetup = ::xcb_get_setup(hDisplay);
			assert(pDisplaySetup != NULL);
			xcb_screen_iterator_t iScreen = ::xcb_setup_roots_iterator(pDisplaySetup);
			for (int i = 0; i < iScreenPreferred; ++i)
			{
				::xcb_screen_next(&iScreen);
			}
			//CreateWindowExW
			xcb_window_t XID = ::xcb_generate_id(hDisplay);
			uint32_t valuemaskCW = XCB_CW_EVENT_MASK;
			uint32_t valuelistCW[1] = { XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_EXPOSURE };
			xcb_void_cookie_t cookieCW = ::xcb_create_window_checked(hDisplay, XCB_COPY_FROM_PARENT, XID, iScreen.data->root, 0, 0, iScreen.data->width_in_pixels, iScreen.data->height_in_pixels, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, valuemaskCW, valuelistCW);
			xcb_generic_error_t *pErrorCW = ::xcb_request_check(hDisplay, cookieCW);//隐式xcb_flush
			if (pErrorCW != NULL)
			{
				hWnd = ~0U;
			}
			else
			{
				//ShowWindow
				xcb_void_cookie_t cookieMW = ::xcb_map_window_checked(hDisplay, XID);
				xcb_generic_error_t *pErrorMW = ::xcb_request_check(hDisplay, cookieMW);//隐式xcb_flush
				if (pErrorMW != NULL)
				{
					hWnd = ~0U;
				}
				else
				{
					hWnd = XID;
				}
			}
		}
	}

	if (hWnd == ~0U)
	{
		::xcb_disconnect(hDisplay);
		return 1;
	}

	xcb_key_symbols_t * pKeySymbolTable = ::xcb_key_symbols_alloc(hDisplay);
	assert(pKeySymbolTable != NULL);

	PTWindowImpl g_WindowImpl;
	g_WindowImpl.Construct();

	g_WindowImpl.HandleSet(hDisplay,hWnd);

	xcb_generic_event_t *pGenericEvent;
	//xcb_get_file_descriptor
	//select
	while (pGenericEvent = ::xcb_wait_for_event(hDisplay), pGenericEvent != NULL)
	{
		switch (pGenericEvent->response_type)
		{
		case XCB_EXPOSE:
		{
			xcb_expose_event_t *pExposeEvent = reinterpret_cast<xcb_expose_event_t *>(pGenericEvent);
			if (pExposeEvent->window == hWnd)
			{
				g_WindowImpl.SizeSet(pExposeEvent->width, pExposeEvent->height);
			}
		}
		break;
		case XCB_KEY_PRESS:
		{
			xcb_key_press_event_t *pKeyPressEvent = reinterpret_cast<xcb_key_press_event_t *>(pGenericEvent);
			if (pKeyPressEvent->event == hWnd)
			{
				switch (::xcb_key_symbols_get_keysym(pKeySymbolTable, pKeyPressEvent->detail, 0))
				{
				case XK_W:
				case XK_w:
				{
					//W
				}
				break;
				}
			}
		}
		break;
		case XCB_MAPPING_NOTIFY:
		{
			xcb_mapping_notify_event_t *pMappingNotifyEvent = reinterpret_cast<xcb_mapping_notify_event_t *>(pGenericEvent);
			int iResult = ::xcb_refresh_keyboard_mapping(pKeySymbolTable, pMappingNotifyEvent);
			assert(iResult == 0);
		}
		break;
		default:
			//Unknown event type, ignore it
			break;
		}
		::free(pGenericEvent);//xcb频繁访问堆——低效的！！！
	}

	::xcb_key_symbols_free(pKeySymbolTable);

	::xcb_disconnect(hDisplay);

	g_WindowImpl.Destruct();
	return 0;
}
